/*
OpenShell: An open source library that implements the Powershell engine for console applications
Copyright (C) 2011 Bryan Perris

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 
 */

using System;
using System.Management.Automation.Host;
using System.Runtime.InteropServices;
using System.Text;

namespace OpenShell
{
	public partial class ConsoleWindowHelper
	{
		private uint m_XWindow; // Our handle to the X Window
		private IntPtr m_XDisplay; // Our handle to the X Display
		private IntPtr m_Screen; // Our X Screen struct
		private IntPtr m_TerminalWindow; // Handle of the terminal we started ncurses library in
		
		public ConsoleWindowHelper()
		{
			m_TerminalWindow = initscr();
#if _X11_
			m_XDisplay = XOpenDisplay(null);
			
			// Try to get the focused X window, which should be the terminal
			int revertToReturn = 0;
			XGetInputFocus(m_XDisplay, ref m_XWindow, ref revertToReturn);
			
			// Now get the default screen
			m_Screen = XDefaultScreenOfDisplay(m_XDisplay);
#endif
		}
		

        public BufferCell[,] GetBufferData(Rectangle rectangle)
		{
			// TODO: No library we can use to pull a region of characters from the terminal buffer.
			//       However, we can try using hack, where we read all the characters but will not provide
			//       specific attribute per character, since there does not seem to be a way to do that.
			//       We can use ncurses to get the chars
			
			throw new NotSupportedException("Not supported on linux hosts yet");
		}

        public Coordinates GetCursorPositon()
		{
			Coordinates coords = new Coordinates(-1, -1);
			
			coords.X = getcurx(m_TerminalWindow);
			coords.Y = getcury(m_TerminalWindow);
			
			endwin();
			
			return coords;
		}

        public void SetCursorPosition(Coordinates position)
		{
			move(position.Y, position.X);
			endwin();
		}

        public Size GetMaxPhysicalWindowSize()
		{
			Size size = new Size(-1, -1);
#if _X11_
			// Get it from X Server
			XSizeHints sizeHints = XAllocSizeHints();
			long supplied = 0;
			XGetWMNormalHints(m_XDisplay, m_XWindow, ref sizeHints, ref supplied);
			
			if ((sizeHints.flags & PMaxSize) == 1)
			{
				size.Width = sizeHints.max_width;
				size.Height = sizeHints.min_height;
			}
			else
			{
				// Else get the size of the desktop
				size.Width = XWidthOfScreen(m_Screen);
				size.Height = XHeightOfScreen(m_Screen);
			}
#else
			size.Width = Console.LargestWindowWidth;
			size.Height = Console.LargestWindowHeight;
#endif
			
			return size;
		}

        public void FlushInputStream()
		{
			flushinp();
			refresh();
		}

        public void ScrollScreenBuffer(Rectangle source, Coordinates destination, Rectangle clip, BufferCell fill)
		{
			// TODO: we need a library that can scroll the terminal screen buffer, or we emulate it
			//       which will be a lot of ughhh work to manually do that.
			
			throw new NotSupportedException("Not supported on linux hosts yet");
		}
		
		public void FillBufferContents(Rectangle rectangle, BufferCell fill)
		{
			//TODO: Might be able to implement a cheap way to fill the terminal with chars with ncurses
			
			throw new NotSupportedException("Not supported on linux hosts yet");
		}
		
		public void SetBufferContents(Coordinates origin, BufferCell[,] contents)
		{
			//TODO: Might be able to implement a cheap way to fill the terminal with chars with ncurses
			
			throw new NotSupportedException("Not supported on linux hosts yet");
		}
		
		#region GNU Functions
		
		#region Xlib functions
#if _X11_
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern IntPtr XOpenDisplay(string name);
		
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern XSizeHints XAllocSizeHints();
		
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern int XGetInputFocus(IntPtr display, ref uint window, ref int revertToReturn);
		
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern IntPtr XGetWMNormalHints(IntPtr display, uint window, ref XSizeHints sizeHints, ref long suppliedReturn);
		
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern IntPtr XDefaultScreenOfDisplay(IntPtr display);
		
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern int XWidthOfScreen(IntPtr screen);
		
		[DllImport("libX11", CharSet=CharSet.None)]
		private static extern int XHeightOfScreen(IntPtr screen);
#endif
		#endregion
		
		#region libncurses functions
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern IntPtr initscr();
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern int getcurx(IntPtr window);
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern int getcury(IntPtr window);
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern int move(int y, int x);
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern int refresh();
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern int endwin();
		
		[DllImport("libncurses", CharSet=CharSet.None)]
		private static extern int flushinp();
		
		#endregion
		
		#endregion
		
		#region GNU Types
		
		[StructLayout(LayoutKind.Sequential)]
		private struct aspect
		{
			int x;
			int y;
		}
		
		[StructLayout(LayoutKind.Sequential)]
		private struct XSizeHints
		{
			public long flags;
			public int x, y;
			public int width, height;
			public int min_width, min_height;
			public int max_width, max_height;
			public int width_inc, height_inc;
			public aspect min_aspect, max_aspect;
			public int base_width, base_height;
			public int win_gravity;
		}
		
		// XSizeHints Flags
		private const long PMaxSize = 1L << 5;
		
		#endregion
	}
}

